#include "GlobalHead.h"
#include "sceneObject.h"
#include "CustomPlayer.h"
#include "PropertyFile.h"
#include <Base/Reader.h>

using namespace App;
PROPERTY_SOURCE(Part::sceneObject, Part::Primitive)
using namespace Part;

const char* sceneObject::ShadowEnums[]= {"As Nothing","As ShadowCaster","As ShadowReceiver",NULL};
sceneObject::sceneObject(void)
:isGet(false),pChara(NULL)
{
	ADD_PROPERTY_TYPE(Length,(1.0f),"Box",App::Prop_None,"The length of the box");
	ADD_PROPERTY_TYPE(Width ,(1.0f),"Box",App::Prop_None,"The width of the box");
	ADD_PROPERTY_TYPE(Height,(1.0f),"Box",App::Prop_None,"The height of the box");
	//ADD_PROPERTY(ShadowSel,((long)0));
	ADD_PROPERTY_TYPE(ShadowSel,((long)0),"shadow",App::Prop_None,"shadow of object");
	ShadowSel.setEnums(ShadowEnums);

	ADD_PROPERTY_TYPE(MaterialList,((long)0),"Texture",App::Prop_None,"List of object material");
	ADD_PROPERTY_TYPE(TextureFile,(0),"Texture",App::Prop_None,"object texture");
	

	//ADD_PROPERTY_TYPE(VrmlFile,(0),"mm",App::Prop_None,"Included file with the VRML definition");
	//ADD_PROPERTY(testfrile,(0),"mm",App::Prop_None,"Included file with the VRML definition");


}
	
sceneObject::~sceneObject(void)
{
  MaterialList.setEnums(0);
  CCustomPlayer::Instance().DeleteObjectFromScene(this->Shape.getValue()/*->GetID()*/);
}


short sceneObject::mustExecute() const	
{
     if (Length.isTouched() ||
         Height.isTouched() ||
         Width.isTouched() )
         return 1;
    return Primitive::mustExecute();
}
// void sceneObject::Save(Base::Writer &writer)
// {
// 	const char* pn = static_cast<App::PropertyFileIncluded*>(getPropertyByName("VrmlFile"))->getValue();
// 	CCustomPlayer::Instance->save(pn,pChara);
// 	App::DocumentObject::Save(writer);
// 
// }
App::DocumentObjectExecReturn *sceneObject::execute(void)
{
// 	 if(pChara == NULL)
// 		return App::DocumentObject::StdReturn;

     double L = Length.getValue();
     double W = Width.getValue();
     double H = Height.getValue();
// 
     if (L < 0.1f)
         return new App::DocumentObjectExecReturn("Length of box too small");
 
     if (W < 0.1f)
         return new App::DocumentObjectExecReturn("Width of box too small");
 
     if (H <0.1f) 
         return new App::DocumentObjectExecReturn("Height of box too small");
	    
	   if (isImporting())
	   {
 		   const char* filename = this->pathName.getValue();
 		   pChara = /*static_cast<CKCharacter*>*/(CCustomPlayer::Instance().ImportObject(filename));
 		   this->Shape.setValue(pChara);
	   }


		

   // VxVector scale(L,W,H);

	 // pChara->SetScale(&scale);
// 
//     try {
//         // Build a box using the dimension attributes
//         BRepPrimAPI_MakeBox mkBox(L, W, H);
//         TopoDS_Shape ResultShape = mkBox.Shape();
//         this->Shape.setValue(ResultShape);
//     }
//     catch (Standard_Failure) {
//         Handle_Standard_Failure e = Standard_Failure::Caught();
//         return new App::DocumentObjectExecReturn(e->GetMessageString());
//     }

    return App::DocumentObject::StdReturn;
}

/**
 * This method was added for backward-compatibility. In former versions
 * of Box we had the properties x,y,z and l,h,w which have changed to
 * Location -- as replacement for x,y and z and Length, Height and Width.
 */
 
 void sceneObject::Restore(Base::XMLReader &reader)
 {
      reader.readElement("Properties");
      int Cnt = reader.getAttributeAsInteger("Count");
  
       bool location_xyz = false;
       bool location_axis = false;
       bool distance_lhw = false;
       Base::Placement plm;
       App::PropertyDistance x,y,z;
       App::PropertyDistance l,w,h;
       App::PropertyVector Axis, Location;
       Axis.setValue(0.0f,0.0f,1.0f);
       for (int i=0 ;i<Cnt;i++) {
           reader.readElement("Property");
           const char* PropName = reader.getAttribute("name");
           const char* TypeName = reader.getAttribute("type");
           App::Property* prop = getPropertyByName(PropName);
           if (!prop) {
               // in case this comes from an old document we must use the new properties
               if (strcmp(PropName, "l") == 0) {
                   distance_lhw = true;
                   prop = &l;
               }
               else if (strcmp(PropName, "w") == 0) {
                   distance_lhw = true;
                   prop = &h; // by mistake w was considered as height
               }
               else if (strcmp(PropName, "h") == 0) {
                   distance_lhw = true;
                   prop = &w; // by mistake h was considered as width
               }
               else if (strcmp(PropName, "x") == 0) {
                   location_xyz = true;
                   prop = &x;
               }
               else if (strcmp(PropName, "y") == 0) {
                   location_xyz = true;
                   prop = &y;
               }
               else if (strcmp(PropName, "z") == 0) {
                   location_xyz = true;
                   prop = &z;
               }
               else if (strcmp(PropName, "Axis") == 0) {
                   location_axis = true;
                   prop = &Axis;
               }
               else if (strcmp(PropName, "Location") == 0) {
                   location_axis = true;
                   prop = &Location;
               }
           }
           else if (strcmp(PropName, "Length") == 0 && strcmp(TypeName,"PropertyDistance") == 0) {
               distance_lhw = true;
               prop = &l;
           }
           else if (strcmp(PropName, "Height") == 0 && strcmp(TypeName,"PropertyDistance") == 0) {
               distance_lhw = true;
               prop = &h;
           }
           else if (strcmp(PropName, "Width") == 0 && strcmp(TypeName,"PropertyDistance") == 0) {
               distance_lhw = true;
               prop = &w;
           }
   
           // NOTE: We must also check the type of the current property because a subclass
           // of PropertyContainer might change the type of a property but not its name.
           // In this case we would force to read-in a wrong property type and the behaviour
           // would be undefined.
           std::string tn = TypeName;
           if (strcmp(TypeName,"PropertyDistance") == 0) // missing prefix App::
               tn = std::string("App::") + tn;
           if (prop && strcmp(prop->getTypeId().getName(), tn.c_str()) == 0)
               prop->Restore(reader);
   
           reader.readEndElement("Property");
       }
   
       if (distance_lhw) {
           this->Length.setValue(l.getValue());
           this->Height.setValue(h.getValue());
           this->Width.setValue(w.getValue());
       }
   
       // for 0.7 releases or earlier
        if (location_xyz) {
            plm.setPosition(Base::Vector3d(x.getValue(),y.getValue(),z.getValue()));
            this->Placement.setValue(this->Placement.getValue() * plm);
            this->Shape.StatusBits.set(10); // override the shape's location later on
        }
        // for 0.8 releases
        else if (location_axis) {
           Base::Vector3f d = Axis.getValue();
           Base::Vector3f p = Location.getValue();
            Base::Rotation rot(Base::Vector3d(0.0,0.0,1.0),
                          Base::Vector3d(d.x,d.y,d.z));
           plm.setRotation(rot);
            plm.setPosition(Base::Vector3d(p.x,p.y,p.z));
            this->Placement.setValue(this->Placement.getValue() * plm);
            this->Shape.StatusBits.set(10); // override the shape's location later on
        }
   
       reader.readEndElement("Properties");
 
 }

void sceneObject::onChanged(const App::Property* prop)
{
     if (prop == &Length || prop == &Width || prop == &Height) {
         if (!isRestoring()) {
             App::DocumentObjectExecReturn *ret = recompute();
             delete ret;
        }
     }
      else if (prop == &this->Shape) {

		  if(isImporting() /*|| this->Shape.isLoad()*/)
		  {
            updateMaterial();
		  }
         // see Box::Restore
		 if (this->Shape.StatusBits.test(10)) {
              this->Shape.StatusBits.reset(10);
              App::DocumentObjectExecReturn *ret = recompute();
              delete ret;
              return;
          }
      }
	  else if(prop == &MaterialList)
	  {
         if(Shape.getValue() != NULL)
		{
			isGet = true;
			matName = MaterialList.getValueAsString();
			CKMaterial* selMat = getMaterialbyName(matName);
			CKTexture* curtex = selMat->GetTexture();
			if(curtex)
			{
				char* slotName = curtex->GetSlotFileName(0);
				if(!slotName)
					slotName = "";
				TextureFile.setValue(slotName);
			}else
			{ 
				TextureFile.setValue("");
			}
		 }
		
	  }
	  else if(prop == &TextureFile)
	  {
		  if(isGet)
		  {
			  isGet = false;
		  }else
		  {
			  const char* texName = TextureFile.getValue();
			  CKMaterial* selMat = getMaterialbyName(matName);
			  if(selMat)
			  {
				  CKTexture* tex = selMat->GetTexture();
				  if(tex)
				  selMat->GetTexture()->LoadImage((char*)texName);
			  }

		  }


	  }
	  else if(prop == &ShadowSel)
	  {
		CK3dEntity* cb;
	    if(ShadowSel.getValue() == 0)//As Nothing
		{
			CKAttributeManager* attman = CCustomPlayer::Instance().GetCKContext()->GetAttributeManager();
			CKAttributeType  att = attman->GetAttributeTypeByName("Shadow Caster Receiver");
			cb = this->Shape.getValue();

			if(CKIsChildClassOf(cb,CKCID_CHARACTER))
			{
				CKCharacter* trancam = CKCharacter::Cast(cb);
				for (int i = 0; i < trancam->GetBodyPartCount(); ++i)
				{
					CKBodyPart* bpt = trancam->GetBodyPart(i);
					if(bpt->HasAttribute(att))
						bpt->RemoveAttribute(att);
//					CCustomPlayer::Instance().removeShadowPostRenderCallBack(CK3dEntity::Cast(bpt));
				}
//				CCustomPlayer::Instance().removeShadowPostRenderCallBack((CK3dEntity*)trancam);
				CCustomPlayer::Instance().removeShadowCasterObject((CK3dEntity*)trancam);
			}

// 			cb->RemoveAttribute(att);
// 
// 			CCustomPlayer::Instance().removeShadowPostRenderCallBack(cb);
		}
		else if(ShadowSel.getValue() == 1)//As Shadow Caster
		{
			CKAttributeManager* attman = CCustomPlayer::Instance().GetCKContext()->GetAttributeManager();
			CKAttributeType  att = attman->GetAttributeTypeByName("Shadow Caster Receiver");
			cb = this->Shape.getValue();
		/*	const XObjectPointerArray& oArray = attman->GetGlobalAttributeListPtr (att);*/
			if(CKIsChildClassOf(cb,CKCID_CHARACTER))
			{
				CKCharacter* trancam = CKCharacter::Cast(cb);
				for (int i = 0; i < trancam->GetBodyPartCount(); ++i)
				{
					CKBodyPart* bpt = trancam->GetBodyPart(i);
					if(bpt->HasAttribute(att))
						bpt->RemoveAttribute(att);
			//		CCustomPlayer::Instance().addShadowPostRenderCallBack(CK3dEntity::Cast(bpt));
				}
			//		CCustomPlayer::Instance().addShadowPostRenderCallBack((CK3dEntity*)trancam);
				CCustomPlayer::Instance().addShadowCasterObject((CK3dEntity*)trancam);
			}
		/*	const XObjectPointerArray& oArray1 = attman->GetGlobalAttributeListPtr (att);*/
	/*		int s = 0;*/
// 			CKBOOL succ = cb->RemoveAttribute(att);
// 
// 			CCustomPlayer::Instance().addShadowPostRenderCallBack(cb);
		}
		else if(ShadowSel.getValue() == 2)//As Shadow Receiver
		{
			CKAttributeManager* attman = CCustomPlayer::Instance().GetCKContext()->GetAttributeManager();
			CKAttributeType  att = attman->GetAttributeTypeByName("Shadow Caster Receiver");
            cb = this->Shape.getValue();
/*			int ct= cb->GetAttributeCount();*/
			if(CKIsChildClassOf(cb,CKCID_CHARACTER))
			{
//                CKCharacter* trancam = CKCharacter::Cast(cb);
// 			   int bc = trancam->GetBodyPartCount();
// 			   CKBodyPart* bpt = trancam->GetBodyPart(0);
// 			   bc = bpt->GetAttributeCount();
// 			   CKBodyPart* rootbpt =  trancam->GetRootBodyPart();
//                 CKBOOL succ = bpt->SetAttribute(att);
// 				bc = bpt->GetAttributeCount();
// 			    succ = trancam->SetAttribute(att/*attman->GetAttributeTypeByName("Shadow Caster Receiver")*/);
//                ct= cb->GetAttributeCount();
// 		      CKBOOL ttt = TRUE;
				CKCharacter* trancam = CKCharacter::Cast(cb);
				for (int i = 0; i < trancam->GetBodyPartCount(); ++i)
				{
					CKBodyPart* bpt = trancam->GetBodyPart(i);
					CKBOOL succ = bpt->SetAttribute(att);
			//		CCustomPlayer::Instance().removeShadowPostRenderCallBack(CK3dEntity::Cast(bpt));
				}
			//	CCustomPlayer::Instance().removeShadowPostRenderCallBack((CK3dEntity*)trancam);
				CCustomPlayer::Instance().removeShadowCasterObject((CK3dEntity*)trancam);
			}

		//	CKBOOL has = cb->HasAttribute(attman->GetAttributeTypeByName("Shadow Caster Receiver"));
	/*		const XObjectPointerArray& oArray = attman->GetGlobalAttributeListPtr (att);*/
	/*		int s = 0;*/
		}
	  }
    Part::Primitive::onChanged(prop);
}

CKMaterial* sceneObject::getMaterialbyName(const char* mn)
{
	std::vector<MatArray>::iterator it = aDisplayModesArray.begin();
	for(; it != aDisplayModesArray.end(); it++)
	{
		if(strcmp(it->materialName.c_str(),mn) == 0)
       return it->curMaterial;
	}
	return NULL;
}
void sceneObject::updateMaterial()
{
	// 	pCharacter = this->getCharacter();
	// 	if(pCharacter == NULL)
	// 		return;
	// Retrieve the supported display modes of the view provider
	aDisplayModesArray = this->getDisplayModes();

	if( aDisplayModesArray.empty() )
	{
		MatArray ma;
		ma.curMaterial = NULL;
		ma.materialName = "kong";
		aDisplayModesArray.push_back(ma);
	}else
	{
		// We must collect the const char* of the strings and give it to PropertyEnumeration,
		// but we are still responsible for them, i.e. the property class must not delete the literals.
		for ( std::vector<MatArray>::iterator it = aDisplayModesArray.begin(); it != aDisplayModesArray.end(); ++it ) {
			aDisplayEnumsArray.push_back(it->materialName.c_str());
		}
		aDisplayEnumsArray.push_back(0); // null termination
		MaterialList.setEnums(&(aDisplayEnumsArray[0]));

		// set the active mode
		// 	const char* defmode = this->getDefaultDisplayMode();
		// 	if (defmode)
	 			MaterialList.setValue(aDisplayModesArray[0].materialName.c_str());
	}

}
std::vector<MatArray> sceneObject::getDisplayModes(void) const
{
	std::vector<MatArray> StrList;

	CKCharacter* pcha = static_cast<CKCharacter*>(this->Shape.getValue());
	if(pcha)
	{
		CKCharacter* pc = pcha;
		int bodyCount = pc->GetBodyPartCount();
		for (int i = 0; i<bodyCount; i++)
		{
			CKBodyPart* bp = pc->GetBodyPart(i);
			if(bp)
			{
				int mc = bp->GetMeshCount();
				for(int m = 0; m<mc; m++)
				{
					CKMesh* bMesh = bp->GetMesh(m);
					if(bMesh)
					{
						int matcount = bMesh->GetMaterialCount();
						for(int n = 0; n< matcount; n++)
						{
							CKMaterial* bMat = bMesh->GetMaterial(n);

							if(bMat)
							{
								MatArray ma;
								ma.curMaterial = bMat;
								ma.materialName = bMat->GetName();
								StrList.push_back(ma);
							}
						}
					}

				}
			}
		}
	}

	return StrList;
}


